----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    14:45:41 04/01/2011 
-- Design Name: 
-- Module Name:    controlUnit - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
-- 
--  Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
library mylib;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use mylib.definitions.all;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity controlUnit is
    Port ( clk_i, 						   		 			-- global clock
			  clr_i,                      		 			-- global reset
			  int_req,                    		 			-- interrupt request		  
			  data_ack_i,								 			-- data ack
			  instr_ack_i,		            		 			-- instruction ack
			  port_ack_i  		: in STD_LOGIC;   		 	-- I/O ack		  
			  opcode_in   		: in op_code;          		-- instruction opcode (from datapath)
			  opcode_comp_in	: in op_code_comp;	 		-- instruction opcode comparator (from datapath)	       
			  cod_ext3_in		: in ext3;						-- instruction miscellaneus extension code
			  proc_state  		: out processor_state;		-- processor state
			  reg_file_we,							       		-- register file write enable  
			  PC_en,													-- Program Counter enableado	
			  enable_IR,											-- IR is enableado
			  enable_acc,											-- ACC is enableado
			  enable_data,											-- Data is enableado
			  int_ack,												--	interruption akc
			  pc_reset,												-- para introducir un 0 en el pc
			  --enable_flags,										-- Flags enableado
			  is_call,												--	Para la carga del contador de programa en la pila!!!
			  is_immediate,										-- immediate instruction
			  is_branch,                         			-- is_branch decoded 
			  is_jump,                           			-- is_jump decoded
			  is_misc,												-- is_misc enabled
			  is_reti,                           			-- return from input decoded
			  is_load,												-- load instruction
			  is_store,												-- store instruction
			  is_alu,												-- alu instruction
			  is_shift,												-- shift
			  is_in,													-- is in instruction
			  is_out,
			  is_ret      		: out STD_LOGIC);      		-- return from subroutine decoded
end controlUnit;

architecture Behavioral of controlUnit is

	signal state, next_state : processor_state;
	--signal is_port_in, is_port_out: std_logic;
	signal is_interrupt, is_wait, is_stdby, is_mem, is_out_aux,is_branch_aux, is_in_aux, is_jump_aux, is_misc_aux, is_store_aux, is_alu_aux, is_shift_aux,
					is_load_aux : STD_LOGIC;
	
begin

	SYNC_PROC: process (clk_i)
   begin
      if (clk_i'event and clk_i = '1') then
         if (clr_i = '1') then
            state <= fetch;   
         else
            state <= next_state;
			end if;        
      end if;
   end process;
	

	OUTPUT_DECODE: process (state, is_in_aux, is_out_aux, is_branch_aux,is_jump_aux, is_misc_aux, is_store_aux, is_alu_aux, is_shift_aux, is_load_aux)
   begin
		proc_state <= state;
		is_branch <= is_branch_aux;
		is_jump <= is_jump_aux;
	   is_in <= is_in_aux;
		is_misc <= is_misc_aux;
		is_store <= is_store_aux;
		is_alu <= is_alu_aux;
		is_shift <= is_shift_aux;
		is_load <= is_load_aux;
		is_out <= is_out_aux;
		
				
      --ESTADO FETCH
		if (state = fetch)  then
				enable_IR <= '1';
      else
            enable_IR <= '0';				
      end if;		
		
		--ESTADO DECODE
		if (state = decode) then
      case opcode_in is
			when opc_alu_reg 			=> is_alu_aux<='1';
												is_shift_aux<='0';
												is_load_aux<='0';
												is_mem<='0';
												is_store_aux<='0';
												is_in_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_call <= '0';
												is_reti<='0';	
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='0';
												
			when opc_alu_imm_add 	=>	is_alu_aux<='1';
												is_shift_aux<='0';
												is_load_aux<='0';
												is_mem<='0';
												is_store_aux<='0';
												is_in_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';	
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='1';

			when opc_alu_imm_addc 	=>	is_alu_aux<='1';
												is_shift_aux<='0';
												is_load_aux<='0';
												is_mem<='0';
												is_store_aux<='0';
												is_in_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='1';
												
			when opc_alu_imm_sub 	=>	is_alu_aux<='1';
												is_shift_aux<='0';
												is_load_aux<='0';
												is_mem<='0';
												is_store_aux<='0';
												is_in_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='1';
												
			when opc_alu_imm_subc 	=>	is_alu_aux<='1';
												is_shift_aux<='0';
												is_load_aux<='0';
												is_mem<='0';
												is_store_aux<='0';
												is_in_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';	
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='1';
												
			when opc_alu_imm_and 	=>	is_alu_aux<='1';
												is_shift_aux<='0';
												is_load_aux<='0';
												is_mem<='0';
												is_store_aux<='0';
												is_in_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';	
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='1';
												
			when opc_alu_imm_or 		=>	is_alu_aux<='1';
												is_shift_aux<='0';
												is_load_aux<='0';
												is_mem<='0';
												is_store_aux<='0';
												is_in_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';	
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='1';
												
  			when opc_alu_imm_xor 	=>	is_alu_aux<='1';
												is_shift_aux<='0';
												is_load_aux<='0';
												is_mem<='0';
												is_store_aux<='0';
												is_in_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';	
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='1';
												
  			when opc_shift_rot		=>	is_shift_aux<='1';
												is_alu_aux<='0';
												is_load_aux<='0';
												is_mem<='0';
												is_store_aux<='0';
												is_in_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='0';
												
  			when opc_mem_io_ld		=>	is_load_aux<='1';
												is_mem<='1';
												is_shift_aux<='0';
												is_alu_aux<='0';
												is_store_aux<='0';
												is_in_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';	
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='0';
												
  			when opc_mem_io_st		=>	is_mem<='1';
												is_store_aux<='1';
												is_load_aux<='0';
												is_shift_aux<='0';
												is_alu_aux<='0';
												is_in_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';	
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='0';
												
  			when opc_mem_io_in		=>	is_mem<='1';
												is_in_aux<='1';
												is_store_aux<='0';
												is_load_aux<='0';
												is_shift_aux<='0';
												is_alu_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';	
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='0';
												
			when opc_mem_io_out		=>	is_mem<='1';
												is_out_aux<='1';
												is_in_aux<='0';
												is_store_aux<='0';
												is_load_aux<='0';
												is_shift_aux<='0';
												is_alu_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';	
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='0';
												
			when opc_jumps				=> is_alu_aux<='0';
												is_shift_aux<='0';
												is_load_aux<='0';
												is_mem<='0';
												is_store_aux<='0';
												is_in_aux<='0';
												is_out_aux<='0';
												is_branch_aux<='0';
												is_jump_aux<='0';
												is_misc_aux<='0';
												is_ret<='0';
												is_reti<='0';	
												is_call <= '0';
												is_wait<='0';
												is_stdby<='0';
												is_immediate<='0';
												case opcode_comp_in is
													when opc_comp_branch		=>	is_branch_aux<='1';
													when opc_comp_jump_call	=>	is_jump_aux<='1';
													when opc_comp_misc		=>	is_misc_aux<='1';
														  case cod_ext3_in is
																when ext_misc_ret 	=>	is_ret<='1';		
																when ext_misc_reti 	=>	is_reti<='1';					
																when ext_misc_wait 	=>	is_wait<='1';		
																when ext_misc_stdby  =>	is_stdby<='1';
																when others				=> is_ret<='0';		
																								is_reti<='0';					
																								is_wait<='0';		
																								is_stdby<='0';
															end case;
													when others					=>	is_branch_aux<='0';
																						is_jump_aux<='0';
																						is_misc_aux<='0';
												end case;
												
			when others				   => null;
			end case;
      end if;
		
		
		--ESTADO EXECUTE
		if (state = execute) then
         enable_acc <='1';
			enable_data <='1';
      else
         enable_acc <='0';
			enable_data <='0';
		end if;
		
		--ESTADO is_mem
		if (state = mem ) then
		--do nothing
      else
		--rest in peace
      end if;
		
		--ESTADO WRITE_BACK
		if (state = write_back) then
         PC_en<='1';
			reg_file_we<='1';
      else
         PC_en<='0';
			reg_file_we<='0';
      end if;
				
		--Estado INTERRUPT
		if (state = interrupt) then
         is_call <= '1';
			pc_reset <= '1';
			is_interrupt <='1';
		else
			is_call <= '0';
			pc_reset <= '0';
			if(is_interrupt='1') then
				int_ack <= '1';
				is_interrupt <= '0';
			else
				int_ack <= '0';
			end if;
		end if;
   end process;
	
	NEXT_STATE_DECODE: process (state, instr_ack_i, data_ack_i, port_ack_i, is_branch_aux, is_jump_aux, is_interrupt, is_misc_aux, is_wait, is_stdby, is_mem, is_store_aux, is_out_aux, is_alu_aux, is_shift_aux, is_in_aux, is_load_aux)
   begin
	
      next_state <= state;  --default is to stay in current state
      case (state) is
						
						
			--ESTAMOS EN FETCH
         when fetch =>
				if instr_ack_i = '0' then
               next_state <= fetch;
				else 
               next_state <= decode;				
            end if;
				
			--ESTAMOS EN DECODE
         when decode =>
				if is_misc_aux='1' and (is_wait='1' or is_stdby='1') and (not int_req='1')  then
               next_state <= decode;
				elsif	is_alu_aux='1' or is_shift_aux='1' or is_mem='1' then
               next_state <= execute;
            elsif ((is_branch_aux='1' or is_jump_aux='1') and int_req='1') or (is_misc_aux='1' and int_req='1') then
               next_state <= interrupt;
            elsif((is_branch_aux='1' or is_jump_aux='1') and (not int_req='1')) or (is_misc_aux='1' and not(is_wait='1' or is_stdby='1') and (not int_req='1')) then
               next_state <= fetch;
            else
				   next_state <= decode;
				end if;	
			
			
			--ESTAMOS EN EXECUTE
			when execute =>
            if (is_mem='1' and is_store_aux='1' and data_ack_i='1' and int_req='1') or (is_mem='1' and is_out_aux='1' and port_ack_i='1' and int_req='1') then
               next_state <= interrupt;
            elsif (is_mem='1' and is_load_aux='1' and data_ack_i='1') or (is_mem='1' and is_in_aux='1' and port_ack_i='1') or (not is_mem='1') then
               next_state <= write_back;
            elsif((is_mem='1' and (is_load_aux='1' or is_store_aux='1') and (not data_ack_i='1')) or (is_mem='1' and (is_in_aux='1' or is_out_aux='1') and (not port_ack_i='1')))  then
               next_state <= mem;
            elsif (is_mem='1' and is_store_aux='1' and data_ack_i='1' and (not int_req='1')) or (is_mem='1' and is_out_aux='1' and port_ack_i='1' and (not int_req='1')) then
               next_state <= fetch;
            else
				   next_state <= execute;
            end if;

			--ESTAMOS EN WRITE_BACK
			when write_back =>
            if int_req = '1' then
               next_state <= interrupt;
            elsif not int_req = '1' then 
               next_state <= fetch;
				else
					next_state <= write_back;
				end if;	
			
			--ESTAMOS EN INTERRUPT
			when interrupt =>
            next_state <= fetch;
            

			--ESTAMOS EN MEM
			when mem =>
            if ((is_load_aux='1' or is_store_aux='1') and (not data_ack_i='1')) or ((is_in_aux='1' or is_out_aux='1') and (not port_ack_i='1')) then
               next_state <= mem;
            elsif (is_load_aux='1' and data_ack_i='1') or (is_in_aux='1' and port_ack_i='1') then
               next_state <= write_back;
            elsif (is_store_aux='1' and data_ack_i='1' and (not int_req='1')) or (is_out_aux='1' and port_ack_i='1' and (not int_req='1')) then
               next_state <= fetch;
            elsif (is_store_aux='1' and data_ack_i='1' and (int_req='1')) or (is_out_aux='1' and port_ack_i='1' and (int_req='1')) then
               next_state <= interrupt;
            else
				   next_state <= mem;
			   end if;

			--EN CUALQUIER OTRO CASO: vamos a FETCH.
         when others =>
            next_state <= fetch;
      end case;      
   end process;

end Behavioral;

